home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Utilities / OpenHash.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  34.9 KB  |  1,071 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        OpenHash.cpp
  3.  
  4.     Contains:    OpenHashTable, a hash table with "open" hashing
  5.  
  6.     Owned by:    David McCusker
  7.  
  8.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     
  11. */
  12.  
  13. #ifndef _OPENHASH_
  14. #include "OpenHash.h"
  15. #endif
  16.  
  17. #ifndef _EXCEPT_
  18. #include "Except.h"
  19. #endif
  20.  
  21. //==============================================================================
  22. // OpenHashEntry: theory of operation
  23. //==============================================================================
  24.  
  25. /*
  26.     OpenHashEntry is part of the the representation of associations inside
  27.     a hash table.  The most important role that a OpenHashEntry has is
  28.     its index relative to the beginning of a vector of OpenHashEntry's; this
  29.     index is the same index of the key and value in separate vectors.
  30.     Allocating a OpenHashEntry at a given index means that the corresponding
  31.     key and value at the same index are also allocated.
  32. */
  33.  
  34. class OpenHashEntry {    
  35.  
  36. public: // private (to classes in this file) by convention
  37.     OpenHashEntry*  fLink;
  38.  
  39. #ifdef OPENHASH_CACHE_HASH
  40.     ODULong         fHash;
  41. #endif
  42.     
  43. public:
  44.     ~OpenHashEntry()      {  }
  45.     OpenHashEntry()       {  }
  46.     
  47. #ifdef OPENHASH_CACHE_HASH
  48.     void InitEntry(ODULong hash) 
  49.                           { fHash = hash; }
  50.     ODULong Hash() const  { return fHash; }
  51. #endif
  52. };
  53.  
  54. //==============================================================================
  55. // OpenHashVectors: theory of operation
  56. //==============================================================================
  57.  
  58. /*
  59.     OpenHashVectors is a helper class which factors out the allocation
  60.     of vectors for OpenHashTable.  The general intention is to reduce
  61.     code size, by avoiding duplication in OpenHashTable::OpenHashTable(),
  62.     OpenHashTable::ShrinkToFit() and OpenHashTable::Grow().
  63.     
  64.     OpenHashVectors is initialized by OpenHashTable::NewVectors().
  65. */
  66.  
  67. class OpenHashVectors {    
  68.  
  69. public: // private (to classes in this file) by convention
  70.     OpenHashEntry**  fTable;       // hash buckets (numbering fSlots)
  71.     OpenHashEntry*   fEntries;     // block of fSlots entries
  72.     char*            fKeys;        // block of fSlots keys
  73.     char*            fValues;      // block of fSlots values
  74.     OpenHashSize     fSlots;       // current size (capacity) of table
  75.     
  76. public:
  77.     ~OpenHashVectors()      {  }
  78.     OpenHashVectors()       {  }
  79.  
  80.     void Destroy();
  81. };
  82.  
  83. void OpenHashVectors::Destroy()
  84. {
  85.     if (fTable) ODDisposePtr(fTable);
  86.     if (fEntries) ODDisposePtr(fEntries);
  87.     if (fKeys) ODDisposePtr(fKeys);
  88.     if (fValues) ODDisposePtr(fValues);
  89. }
  90.  
  91.  
  92. //==============================================================================
  93. // OpenHashTable
  94. //==============================================================================
  95.  
  96. // OpenHashTable structure
  97. // Here is a picture of a hash table under the following conditions:
  98. // * OPENHASH_CACHE_HASH is not defined.
  99. // * fSlots == 4, fCount == 3 (there is one free slot left in the table)
  100. // * Keys are single bytes, fKeySize == 1 == sizeof(char).
  101. // * Values are shorts (2-byte ints), fValSize == 2 == sizeof(short).
  102. // * The hash of a key is just the byte value of the key.
  103. // * The table contains the following associations (added in this order):
  104. //   <(char)1, (short) 0x10>, <(char)2, (short) 0x20>, <(char)5, (short) 0x50>
  105. // * fEntries begins at address 0x100.
  106. // Note that keys 1 and 5 both hash to the same bucket (1 % 4) == (5 % 4).
  107. //     
  108. // === The PHYSICAL representation of this table is as follows: === 
  109. //
  110. // fTable   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  111. //          |  entry = 0    | entry = 0x108 | entry = 0x104 |  entry = 0    |         
  112. //          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  113. //
  114. //           0x100:          0x104:          0x108:          0x10C:
  115. // fEntries +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  116. //          |  fLink = 0    |  fLink = 0    | fLink = 0x100 |  fLink = 0    |            
  117. //          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  118. //
  119. // fKeys    +---+---+---+---+
  120. //          | 1 | 2 | 5 |???|            
  121. //          +---+---+---+---+
  122. //
  123. // fValues  +---+---+---+---+---+---+---+---+
  124. //          |  0x10 |  0x20 |  0x50 |???????|            
  125. //          +---+---+---+---+---+---+---+---+
  126. //
  127. // fFreeList == 0x10C  // points to last fEntries cell
  128. //
  129. //  === The LOGICAL representation of this table is as follows: === 
  130. //
  131. //      +---+
  132. //      |  /|
  133. //   0  | / |
  134. //      |/  |
  135. //      +---+                108:                100:
  136. //      |   |   +---+---+---+---+   +---+---+---+---+
  137. //   1  | *---->| 5 |  0x50 | *---->| 1 |  0x10 | / |
  138. //      |   |   +---+---+---+---+   +---+---+---+---+
  139. //      +---+                104:
  140. //      |   |   +---+---+---+---+
  141. //   2  | *---->| 2 |  0x20 | / |
  142. //      |   |   +---+---+---+---+
  143. //      +---+
  144. //      |  /|                                    10C:
  145. //   3  | / |                       +---+---+---+---+
  146. //      |/  |          free list -->|???|???????| / |
  147. //      +---+                       +---+---+---+---+
  148. //
  149. // The fEntries slots were allocated sequentially as they were pulled off
  150. // the free list of entries; the slots in fKeys and fValues were allocated
  151. // in parallel with the fEntries slots (key and value allocation is by
  152. // entry index).
  153.  
  154. OpenHashEntry** OpenHashTable::NewTable(OpenHashSize slots) const
  155.     // Assume slots is never zero.  Make a vector of zeroed ptrs.
  156. {
  157.     ODBlockSize size = (ODBlockSize) (slots * sizeof(OpenHashEntry*));
  158.     return (OpenHashEntry**) ODNewPtrClear(size, fHeap);
  159. }
  160.  
  161. char* OpenHashTable::NewKeys(OpenHashSize slots) const
  162.     // Assume slots is never zero.  Make a vector of zeroed space.
  163. {
  164.     ODBlockSize size = (ODBlockSize) (slots * fKeySize);
  165.     return (char*) ODNewPtrClear(size, fHeap);
  166. }
  167.  
  168. char* OpenHashTable::NewValues(OpenHashSize slots) const
  169.     // Assume slots is never zero.  Make a vector of zeroed space.
  170. {
  171.     ODBlockSize size = (ODBlockSize) (slots * fValSize);
  172.     if (size)
  173.         return (char*) ODNewPtrClear(size, fHeap);
  174.     else
  175.         return (char*) 0L;
  176. }
  177.  
  178. OpenHashEntry* OpenHashTable::NewEntries(OpenHashSize slots) const
  179.     // Assume slots is never zero.  Allocate a vector of entries and chain
  180.     // them together to form a linked list with the first elememt at the
  181.     // head and the last element at the tail.  Thus, the whole vector of
  182.     // entries forms the free list, or any number can be removed from the
  183.     // beginning and the remainder become the free list.
  184. {
  185.     ODBlockSize size = (ODBlockSize) (slots * sizeof(OpenHashEntry));
  186.     OpenHashEntry* vector = (OpenHashEntry*) ODNewPtr(size, fHeap);
  187.     if (vector)
  188.     {
  189.         // Chain the entries into a list so vector is the head.
  190.         register OpenHashEntry* v = vector + (slots - 1); // last entry
  191.         v->fLink = 0L; // terminate the end of the list
  192.         while (--v >= vector)
  193.             v->fLink = v + 1;
  194.     }
  195.     return vector;
  196. }
  197.  
  198. ODBoolean OpenHashTable::NewVectors(OpenHashVectors* old, OpenHashSize newSlots)
  199. {
  200.     old->fTable   = fTable;
  201.     old->fEntries = fEntries;
  202.     old->fKeys    = fKeys;
  203.     old->fValues  = fValues;
  204.     old->fSlots   = fSlots;
  205.     
  206.     OpenHashEntry** newTable  = NewTable(newSlots);
  207.     OpenHashEntry* newEntries = NewEntries(newSlots);
  208.     char* newKeys             = NewKeys(newSlots);
  209.     char* newValues           = NewValues(newSlots);
  210.     
  211.     if (newTable && newEntries && newKeys && (newValues || !fValSize))
  212.     {
  213.         // do not modify old member values until all allocation succeeds:
  214.         fTable = newTable;
  215.         fFreeList = fEntries = newEntries;
  216.         fKeys = newKeys;
  217.         fValues = newValues;
  218.         fSlots = newSlots;    
  219.         ++fSeed; // indicate hash table is modified
  220.         return kODTrue;
  221.     }
  222.     else
  223.     {
  224.         if (newTable) ODDisposePtr(newTable);
  225.         if (newEntries) ODDisposePtr(newEntries);
  226.         if (newKeys) ODDisposePtr(newKeys);
  227.         if (newValues) ODDisposePtr(newValues);
  228.         return kODFalse;
  229.     }
  230. }
  231.  
  232. OpenHashEntry** OpenHashTable::Find(void* key, ODULong hash) const
  233. {
  234.     OpenHashEntry** where = fTable + (hash % fSlots);    
  235.     OpenHashEntry* e = *where;    
  236.     while ( e )
  237.     {
  238.         ODULong i = e - fEntries;
  239. #ifdef OPENHASH_CACHE_HASH
  240.         if (e->Hash() == hash && (*fEqual)(fKeys + (i * fKeySize), key))
  241.             return where;
  242. #else
  243.         if ((*fEqual)(fKeys + (i * fKeySize), key))
  244.             return where;
  245. #endif
  246.         where = &e->fLink;
  247.         e = *where;
  248.     }
  249.     return (OpenHashEntry**) 0L;
  250. }
  251.  
  252. inline OpenHashEntry* OpenHashTable::PopFreeEntry()
  253. {
  254.     OpenHashEntry* e = fFreeList;
  255.     if (e)
  256.         fFreeList = e->fLink;
  257.     return e;
  258. }
  259.  
  260. inline void OpenHashTable::PushFreeEntry(OpenHashEntry* e)
  261. {
  262.     e->fLink = fFreeList;
  263.     fFreeList = e;
  264. }
  265.  
  266. void OpenHashTable::Clear()
  267. {
  268.     if (fTable)
  269.     {
  270.         ODDisposePtr(fTable);
  271.         fTable = 0;
  272.     }
  273.     if (fEntries)
  274.     {
  275.         ODDisposePtr(fEntries);
  276.         fEntries = 0;
  277.     }
  278.     if (fKeys)
  279.     {
  280.         ODDisposePtr(fKeys);
  281.         fKeys = 0;
  282.     }
  283.     if (fValues)
  284.     {
  285.         ODDisposePtr(fValues);
  286.         fValues = 0;
  287.     }
  288.     fCount = 0;
  289. }
  290.  
  291. OpenHashTable::~OpenHashTable()
  292. {
  293.     Clear();
  294. }
  295.  
  296. ODBoolean OpenHashTable::StdEqual(const void* key, const void* testKey)
  297.     // A standard equal function useable when keys are pointers or ints.
  298.     // Equality is defined as pointer equality.  Note that since
  299.     // a pointer to the key is passed, the argument type is actually
  300.     // pointer to a pointer.
  301.     // returns ( *(void**) key == *(void**) testKey);
  302. {
  303.     return (*(void**) key == *(void**) testKey);
  304. }
  305.     
  306. ODBoolean OpenHashTable::EqualTwoLongs(const void* key, const void* testKey)
  307.     // A standard equal function for an 8-byte key. Compares the
  308.     // first two longwords of each keys as unsigned longs.
  309. {
  310.     return (*(ODULong*) key == *(ODULong*) testKey) && 
  311.         (((ODULong*) key)[1] == ((ODULong*) testKey)[1]);
  312. }
  313.  
  314. ODULong OpenHashTable::StdHash(const void* key)
  315.     // A standard hash function useable when keys are pointers or ints.
  316.     // Hash is defined as the pointer cast to an integer.  Note that since
  317.     // a pointer to the key is passed, the argument type is actually
  318.     // pointer to a pointer.
  319.     // returns *(ODULong*) key;
  320. {
  321.     return *(ODULong*) key;
  322. }
  323.  
  324. // some constants for HashLong:
  325. enum {
  326.     OpenHashTable_kMersennePrime = 2147483647,  // 0x7FFFFFFF
  327.     OpenHashTable_kMult = 48271L,        // 48271 is a prime number
  328.     OpenHashTable_kQuotient = 44488L,   // quo is mersenne-prime / mult
  329.     OpenHashTable_kRem = 3399L          // rem is mersenne-prime % mult
  330. };
  331.  
  332. ODULong OpenHashTable::Random(register ODULong seed)
  333.     // Pseudo-random number generator with period 2**31-1 (i.e. all
  334.     // values in 1..2**31-1 are visted before repetition). Useful
  335.     // for hashing one or more longwords. Derived from published
  336.     // literature on good random number generators (the names
  337.     // Park and Miller come to mind).
  338. {
  339.     // The idea is to multiply by the multiplier and then mod by
  340.     // mersenne-prime, but in order to avoid 32-bit overflow, a tricky
  341.     // but arithmetically equivalent procedure must be used.  If you
  342.     // allow 32-bit overflow to occur, you kill the nice period as well as
  343.     // other nice attributes of this generator.
  344.     
  345.     if (seed) // avoid generating zero
  346.     {
  347.         long high = seed / OpenHashTable_kQuotient;
  348.         long low = seed % OpenHashTable_kQuotient;
  349.         register long test = 
  350.             (OpenHashTable_kMult * low) - (OpenHashTable_kRem * high);
  351.         seed = (test > 0)?    test : test + OpenHashTable_kMersennePrime;
  352.     }
  353.     else
  354.         seed = 1;
  355.     return seed;
  356. }
  357.  
  358. ODULong OpenHashTable::RandomOneLong(const void* key)
  359.     // A standard hash function for a 4-byte key, based on Random().
  360.     // returns Random( *(ODULong*) key );
  361. {
  362.     return Random( *(ODULong*) key );
  363. }
  364.  
  365. ODULong OpenHashTable::RandomTwoLongs(const void* key)
  366.     // A standard hash function for an 8-byte key, based on Random().
  367.     // returns Random( ((ODULong*) key)[0] ^ ((ODULong*) key)[1] );
  368. {
  369.     return Random( ((ODULong*) key)[0] ^ ((ODULong*) key)[1] );
  370. }
  371.  
  372. ODULong OpenHashTable::HashTwoLongs(const void* key)
  373.     // A standard hash function for an 8-byte key.
  374.     // returns ( ((ODULong*) key)[0] ^ ((ODULong*) key)[1] );
  375. {
  376.     return ( ((ODULong*) key)[0] ^ ((ODULong*) key)[1] );
  377. }
  378.  
  379. enum {
  380.     OpenHashTable_kTop4Mask = 0xF0000000L,
  381.     OpenHashTable_kRightShift = 28
  382. };
  383.  
  384. ODULong OpenHashTable::HashString(register const ODUByte* key, register ODULong keyLength) 
  385.     // Derived from P.J.Weinberger (from the "dragon" book). The left shifts
  386.     // generate values distributed widely across the range of hash values even
  387.     // for short strings, providing good "randomness". The important
  388.     // principle is that by XORing high bits back into the low bits after
  389.     // shifting, we keep the hash dependent on all the bits that have ever
  390.     // been seen so far.
  391. {
  392.     register ODULong hash = 0;
  393.     register ODULong top;
  394.  
  395.     ++keyLength;
  396.     while (--keyLength)
  397.     {
  398.         hash <<= 4;
  399.         hash += (unsigned char) *key++;
  400.         top = (hash & OpenHashTable_kTop4Mask);
  401.         if (top)
  402.         {
  403.             hash ^= (top >> OpenHashTable_kRightShift);
  404.             hash ^= top;
  405.         }
  406.     }
  407.     return hash;
  408. }
  409.  
  410.  
  411. OpenHashTable::OpenHashTable(OpenHashKeyEqual equal, OpenHashKeyHash hash,
  412.               ODMemoryHeapID heap)
  413.   : fTable(0L), fEntries(0L), fKeys(0L), fValues(0L), fFreeList(0L), 
  414.     fEqual(equal), fHash(hash), fHeap(heap),
  415.     fKeySize(0), fValSize(0),
  416.     fSlots(0), fCount(0), fSeed(0), fThrowErrors(kODTrue)
  417.     
  418.         // Compare keys with equal, and hash them with hash.
  419.         // All memory is allocated from the specified heap.
  420. {
  421. }
  422.         
  423. OpenHashTable::OpenHashTable(const OpenHashTable& otherTable)
  424.   : fTable(0L), fEntries(0L), fKeys(0L), fValues(0L), fFreeList(0L), 
  425.     fEqual(otherTable.fEqual), fHash(otherTable.fHash), fHeap(otherTable.fHeap),
  426.     fKeySize(0), fValSize(0),
  427.     fSlots(0), fCount(0), fSeed(0), fThrowErrors(kODTrue)
  428.     // Use the same equal, hash, and heap as otherTable.
  429.     // (Does *not* copy otherTable; use InitializeFromTable() and
  430.     // CopyTable() to actually copy the contents of otherTable.)
  431. {
  432. }
  433.  
  434. void OpenHashTable::SetHeap(ODMemoryHeapID heap)
  435.     // All memory is allocated from the specified heap.
  436.     // This method does nothing after Initialize() has been called.
  437.     // Generally this function is only called by hash tables delegating
  438.     // to this one, which specify the heap in Initialize() functions
  439.     // instead of in the constructor.
  440. {
  441.     if ( !fTable )
  442.         fHeap = heap;
  443. }
  444.  
  445. ODBoolean OpenHashTable::Initialize(ODULong sizeHint, 
  446.                          OpenHashSize keySize,
  447.                          OpenHashSize valueSize, 
  448.                          ODBoolean throwErrors)
  449.     // Initialize the table with sizeHint slots.  Keys will occupy keySize
  450.     // bytes of space, and this amount of space will copied in/out of
  451.     // functions.  Values will occupy valueSize bytes of space, and this
  452.     // amount of space will copied in/out of functions.   valueSize can
  453.     // be zero, causing values to be ignored.  The value of throwErrors
  454.     // controls whether exceptions will be thrown when errors occur.
  455.     // Note that if either keys or values have alignment requirements,
  456.     // then keySize and valueSize must be such that keyPtr + keySize and
  457.     // valuePtr + valueSize point to appropriately aligned structures.
  458.     // Returns false only if memory could not be allocated. (If
  459.     // throwErrors, throws kODErrOutOfMemory instead of returning false.) 
  460.     // You can alternatively use InitAndCopyFrom() to copy another table.
  461. {
  462.     fKeySize = keySize;
  463.     fValSize = valueSize;
  464.     
  465.     fThrowErrors = throwErrors;
  466.     
  467.     Clear(); // just in case this is not the first time.
  468.     ++fSeed;
  469.     
  470.     if (sizeHint < 3)
  471.         sizeHint = 3;
  472.     else if (sizeHint > 0x7FFFF) // avoid huge initial table size
  473.         sizeHint = 0x7FFFF;
  474.     fSlots = sizeHint;
  475.     
  476.     OpenHashVectors old;
  477.     if ( NewVectors(&old, fSlots) )
  478.         return kODTrue;
  479.     else if (throwErrors)
  480.     {
  481.         THROW(kODErrOutOfMemory);
  482.         return kODFalse; // we won't reach here
  483.     }
  484.     else
  485.         return kODFalse;
  486. }
  487.     
  488. ODBoolean OpenHashTable::InitAndCopyFrom(const OpenHashTable& otherTable)
  489.     // Initialize this table so that it uses the same parameters
  490.     // as otherTable; sizeHint is taken as otherTable.Count().
  491.     // Then we call this->CopyTable(otherTable) to copy the contents.
  492.     // Returns false only if memory could not be allocated. (If
  493.     // throwErrors, throws kODErrOutOfMemory instead of returning false.) 
  494. {
  495.     if (Initialize(otherTable.Count(),
  496.                       otherTable.fKeySize,
  497.                       otherTable.fValSize,
  498.                       otherTable.fThrowErrors))
  499.     {
  500.         return this->CopyTable(otherTable);
  501.     }
  502.     else
  503.         return kODFalse;
  504. }
  505.  
  506. void OpenHashTable::RemoveEntry(void* keyPtr)
  507.     // Remove the association with the specified key.  keyPtr must be
  508.     // a POINTER to a key (e.g. if a key is struct Foo, then keyPtr must
  509.     // be type struct Foo*). Nothing happens if there was no such
  510.     // association.  This operation is fast and cheap because an old entry
  511.     // is simply "unlinked" and set aside for reuse.  (Consider using 
  512.     // ShrinkToFit() to reclaim space after numerous removals.)
  513.     // (Note that if you need a copy of the old contents of the key or
  514.     // value from a previous association, you should copy them first using
  515.     // one of GetKey(), GetValue(), or GetKeyAndValue().)
  516. {
  517.     if (fTable)
  518.     {
  519.         OpenHashEntry** where = Find(keyPtr, (*fHash)(keyPtr));
  520.         if (where)
  521.         {
  522.             OpenHashEntry* e = *where;
  523.             *where = e->fLink; // unlink this entry
  524.             PushFreeEntry(e);  // and put it in the free list
  525.             ++fSeed;
  526.             --fCount;
  527.         }
  528.     }
  529. }
  530.  
  531. ODBoolean OpenHashTable::Exists(void* keyPtr) const
  532.     // Return whether there is an association with a key equal to (*keyPtr).
  533.     // keyPtr must be a POINTER to a key (e.g. if a key is struct
  534.     // Foo, then keyPtr must be type struct Foo*).
  535. {
  536.     if (fTable)
  537.         return (Find(keyPtr, (*fHash)(keyPtr)) != 0L);
  538.     return kODFalse;
  539. }
  540.  
  541. void OpenHashTable::CopyTo(void* key, void* value, ODULong i) const
  542. {
  543.     if (key)
  544.         memcpy(key, fKeys + (i * fKeySize), fKeySize);
  545.     if (value && fValSize)
  546.         memcpy(value, fValues + (i * fValSize), fValSize);
  547. }
  548.  
  549. ODBoolean OpenHashTable::GetKeyAndValue(void* key, void* keyPtr, void* valuePtr) const
  550.     // Roughly equivalent to GetKey(key, keyPtr), GetValue(key, valuePtr),
  551.     // but faster.  Either keyPtr or valuePtr can be set to zero to
  552.     // suppress copying that part of the association.
  553. {
  554.     if (fTable)
  555.     {
  556.         OpenHashEntry** where = Find(key, (*fHash)(key));
  557.         if (where)
  558.         {
  559.             CopyTo(keyPtr, valuePtr, (*where) - fEntries);
  560.             return kODTrue;
  561.         }
  562.     }
  563.     return kODFalse;
  564. }
  565.  
  566. ODBoolean OpenHashTable::GetKey(void* keyInPtr, void* keyOutPtr) const
  567.     // Find the key in the table equal to (*keyInPtr) and return it in
  568.     // (*keyOutPtr); returns false only if there was no association with
  569.     // the given key.   keyInPtr and keyOutPtr must be POINTERs to keys
  570.     // (e.g. if a key is struct Foo, then keyPtr must be type struct Foo*).
  571.     // If the association is found, then <key-size> bytes are copied to the
  572.     // address indicated by keyOutPtr; it is safe to use the same pointer
  573.     // for keyInPtr and keyOutPtr because keyInPtr is not read again after
  574.     // keyOutPtr is written.
  575. {
  576.     if (fTable)
  577.     {
  578.         OpenHashEntry** where = Find(keyInPtr, (*fHash)(keyInPtr));
  579.         if (where)
  580.         {
  581.             ODULong i = ((*where) - fEntries); // index of key
  582.             memcpy(keyOutPtr, fKeys + (i * fKeySize), fKeySize);
  583.             return kODTrue;
  584.         }
  585.     }
  586.     return kODFalse;
  587. }
  588.  
  589. ODBoolean OpenHashTable::GetValue(void* keyPtr, void* valuePtr) const
  590.     // Find and return the value associated with key in (*valuePtr);
  591.     // returns false only if there was no association with the given key,
  592.     // or if <value-size> is zero.  keyPtr must be a POINTER to a key, and
  593.     // valuePtr must be a POINTER to a value (e.g. if a key is struct
  594.     // Foo, then keyPtr must be type struct Foo*).
  595.     // If the association is found, and if <value-size> is nonzero, then
  596.     // <value-size> bytes are copied to the address indicated by valuePtr;
  597.     // otherwise valuePtr is ignored.
  598. {
  599.     if (fTable && fValSize)
  600.     {
  601.         OpenHashEntry** where = Find(keyPtr, (*fHash)(keyPtr));
  602.         if (where)
  603.         {
  604.             ODULong i = ((*where) - fEntries); // index of value
  605.             memcpy(valuePtr, fValues + (i * fValSize), fValSize);
  606.             return kODTrue;
  607.         }
  608.     }
  609.     return kODFalse;
  610. }
  611.  
  612. ODBoolean OpenHashTable::ReplaceEntry(void* keyPtr, void* valuePtr)
  613.     // Replace and/or add an entry that associates key with value. keyPtr 
  614.     // must be a POINTER to a key, and valuePtr must be a POINTER to a value
  615.     // (e.g. if a key is struct Foo, then keyPtr must be type struct Foo*).
  616.     // <key-size> bytes from keyPtr will be copied into the table, and 
  617.     // <value-size> bytes from valuePtr will be be copied into the
  618.     // table (if <value-size> is zero, valuePtr is ignored).
  619.     // (Note that if you need a copy of the old contents of the key or
  620.     // value from a previous association, you should copy them first using
  621.     // one of GetKey(), GetValue(), or GetKeyAndValue().)
  622.     // If the addition of this entry makes the table's entry count exceed
  623.     // the current capacity of the table, then the table grows by some
  624.     // percentage (say 50%), leaving some empty slots for future additions.
  625.     // False is returned only if memory could not be allocated. (If
  626.     // throwErrors, throws kODErrOutOfMemory instead of returning false.) 
  627. {
  628.     if (fTable)
  629.     {
  630.         ODULong hash = (*fHash)(keyPtr);
  631.         OpenHashEntry** where = Find(keyPtr, hash);
  632.         if ( !where ) // add a new entry
  633.         {
  634.             OpenHashEntry* e = PopFreeEntry();
  635.             if ( !e )
  636.             {
  637.                 if ( !Grow() )
  638.                 {
  639.                     if (fThrowErrors)
  640.                         THROW(kODErrOutOfMemory);
  641.                     return kODFalse;
  642.                 }
  643.                 e = PopFreeEntry();
  644.             }
  645.             ++fCount;
  646.             where = fTable + (hash % fSlots);
  647.             e->fLink = *where;
  648.             *where = e; // new entries are pushed on front of bucket
  649. #ifdef OPENHASH_CACHE_HASH
  650.             e->InitEntry(hash);
  651. #endif
  652.         }
  653.         ++fSeed; // indicate hash table is modified
  654.         ODULong i = (*where) - fEntries;
  655.         memcpy(fKeys + (i * fKeySize), keyPtr, fKeySize);
  656.         if (fValSize)
  657.             memcpy(fValues + (i * fValSize), valuePtr, fValSize);
  658.     }
  659.     return kODTrue;
  660. }
  661.  
  662.  
  663. ODBoolean OpenHashTable::TrueAction(void* k, void* v, ODULong s, void* r)
  664.     // This function ignores its arguments and always returns true.
  665.     // It is meant to be passed as an action argument to Intersect().
  666. {
  667.     k = v = r = &s; // ignore these
  668.     return kODTrue;
  669. }
  670.  
  671. ODBoolean OpenHashTable::Intersect(OpenHashTable* ht, OpenHashAction action, void* ref)
  672.     // Walk this table and compare with table ht, calling action (with ref
  673.     // as one argument) once for every association in this table which has
  674.     // a key equal to a key in ht.  (Note: the value of slot passed to
  675.     // action is meaningless.) If action ever returns true, Intersect()
  676.     // returns immediately without examining any more associations. 
  677.     // Intersect() returns true only if action returns true (thus, for
  678.     // example, if either table is empty then action is never called and
  679.     // Intersect() returns false). If the caller wishes to build a data
  680.     // structure (e.g. a list) that represents the intersection, this can
  681.     // be maintained in ref.  Or, if the caller wants a boolean yes or no
  682.     // (is the intersection non-empty?) then action merely needs to always
  683.     // return true (use TrueAction()).  Table ht must have the
  684.     // same hash and equal functions as in this table. You cannot add or
  685.     // remove associations in this table during the intersection, but you
  686.     // can in ht. 
  687. {
  688.     OpenHashEntry** bucket = fTable;
  689.     OpenHashEntry** end = fTable + fSlots;
  690.     OpenHashEntry* e;
  691.     OpenHashEntry** where;
  692.     ODULong i; // index of key or value
  693.     char* key;
  694.     char* value = fValues; // nil when fValSize == 0
  695.  
  696.     while (bucket < end)
  697.     {
  698.         e = *bucket++;
  699.         if ( e ) // did we find an old entry in this slot?
  700.         {
  701.             while ( e ) // iterate over all old entries in this old slot
  702.             {
  703.                 i = e - fEntries;
  704.                 key = fKeys + (i * fKeySize);
  705.                 // look for a matching key in ht:
  706. #ifdef OPENHASH_CACHE_HASH
  707.                 where = ht->Find(key, e->Hash());
  708. #else
  709.                 where = ht->Find(key, (*fHash)(key));
  710. #endif
  711.                 if (where)
  712.                 {
  713.                     if (value) 
  714.                         value = fValues + (i * fValSize);
  715.                     if ( (*action)(key, value, /*slot*/ 0, ref) )
  716.                         return kODTrue;
  717.                 }
  718.                 e = e->fLink;
  719.             }
  720.         }
  721.     }
  722.     return kODFalse;
  723. }
  724.  
  725. static ODBoolean 
  726. OpenHashTable_CopyAction(void* key, void* value, ODULong /*slot*/, void* ref)
  727.     // This is the action function used by CopyTable() to copy entries
  728.     // into one table (ref) from another table being walked.  Note that
  729.     // we must invert the sense of the boolean return, because Walk() stops
  730.     // when the first true is returned, but ReplaceEntry() returns *false*
  731.     // on failure (and we only want to stop on failure).
  732. {
  733.     return ( ! ((OpenHashTable*) ref)->ReplaceEntry(key, value));
  734. }
  735.     
  736. ODBoolean OpenHashTable::CopyTable(const OpenHashTable& otherTable)
  737.     // Copy all of otherTable's entries into this table such that the
  738.     // result is the union of the previous contents of this table and
  739.     // otherTable.  If this table was empty, the result is a simple copy.
  740.     // If this table contains entries that are also in otherTable, they
  741.     // are replaced with otherTable's entries (beware losing pointers
  742.     // and creating garbage this way). Othertable should have the same
  743.     // equal and hash functions, as well as the same key and value sizes.  
  744.     // Returns false only if memory could not be allocated. (If
  745.     // throwErrors, throws kODErrOutOfMemory instead of returning false.)
  746.     // The implementation is just an optimized way of iterating over
  747.     // entries in otherTable and calling this->ReplaceEntry() for each. 
  748. {
  749.     // This would be very similar to Walk(), so let's just call Walk()
  750.     // to reduce the size of the code generated.  Note that we must
  751.     // invert the sense of the Walk() return value.
  752.     if (this != &otherTable)
  753.         return ( ! otherTable.Walk(OpenHashTable_CopyAction, (void*) this));
  754.     else
  755.         return kODTrue;
  756. }
  757.  
  758. ODBoolean OpenHashTable::Walk(OpenHashAction action, void* ref) const
  759.     // Call action once for every association in the hash table, passing 
  760.     // through ref each time, as well as the slot in the table containing
  761.     // the association.  If action ever returns true, the walk is terminated
  762.     // immediately, rather than visiting every association. Walk() only
  763.     // returns true if action returns true (thus, an empty table never even
  764.     // calls action and returns false). This function is useful for printing
  765.     // the distribution of keys in a table to evaluate the quality of hashing.
  766.     // (It's not appropriate to expose slots in the table with an iterator.)
  767.     // You cannot add or remove associations during the walk.
  768. {
  769.     OpenHashEntry** bucket = fTable;
  770.     OpenHashEntry** end = fTable + fSlots;
  771.     OpenHashEntry* e;
  772.     ODULong i; // index of key or value
  773.     char* key;
  774.     char* value = fValues; // nil when fValSize == 0
  775.  
  776.     while (bucket < end)
  777.     {
  778.         ODULong slot = bucket - fTable;
  779.         e = *bucket++;
  780.         if ( e ) // did we find an old entry in this slot?
  781.         {
  782.             while ( e ) // iterate over all old entries in this old slot
  783.             {
  784.                 i = e - fEntries;
  785.                 key = fKeys + (i * fKeySize);
  786.                 if (value) 
  787.                     value = fValues + (i * fValSize);
  788.                 if ( (*action)(key, value, slot, ref) )
  789.                     return kODTrue;
  790.                 e = e->fLink;
  791.             }
  792.         }
  793.     }
  794.     return kODFalse;
  795. }
  796.  
  797. ODBoolean OpenHashTable::ShrinkToFit(OpenHashSize extraSlots)
  798.     // Change the capacity of the table to Count() + extraSlots.  This is
  799.     // the principal means of recovering storage after numerous removals.
  800.     // Presumably an application watches the relationship between Count()
  801.     // and Capacity(), and shrinks the table when count falls below some
  802.     // threshold. Returns false only if memory could not be allocated. (If
  803.     // throwErrors, throws kODErrOutOfMemory instead of returning false.)
  804. {
  805.     // This function is very similar to Grow(), except that in general there
  806.     // are unused entries in the old table, and we have to actually iterate over
  807.     // the old table to figure out what to transfer to the new.  So there is not
  808.     // a lot of code in common after the initialization of necessary locals.
  809.  
  810.     if (fTable)
  811.     {
  812.         OpenHashSize nominalSlots = fCount + extraSlots;
  813.         OpenHashSize newSlots = (nominalSlots > 3)? nominalSlots : 3;
  814.         
  815.         OpenHashVectors old;
  816.         if ( !NewVectors(&old, newSlots) )
  817.         {
  818.             if (fThrowErrors)
  819.                 THROW(kODErrOutOfMemory);
  820.             return kODFalse;
  821.         }        
  822.         // iterate over all old entries, copying them to new entries
  823.         // and hashing the new entries into their correct buckets:
  824.     
  825.         OpenHashEntry** oldBucket = old.fTable;
  826.         OpenHashEntry** oldEnd = old.fTable + old.fSlots;
  827.         OpenHashEntry* e;
  828.         OpenHashEntry* olde;
  829.         ODULong n; // new index
  830.         ODULong o; // old index
  831.     
  832.         while (oldBucket < oldEnd)
  833.         {
  834.             olde = *oldBucket++;
  835.             if ( olde ) // did we find an old entry in this slot?
  836.             {
  837.                 while (olde) // iterate over all old entries in this old slot
  838.                 {
  839.                     e = PopFreeEntry();   // pop a new entry
  840.                     n = e - fEntries;
  841.                     o = olde - old.fEntries;
  842.                     
  843.                     char* key = fKeys + (n * fKeySize);
  844.                     memcpy(key, old.fKeys + (o * fKeySize), fKeySize);
  845.                     if (fValSize)
  846.                     {
  847.                         char* value = fValues + (n * fValSize);
  848.                         memcpy(value, old.fValues + (o * fValSize), fValSize);
  849.                     }
  850.                     
  851. #ifdef OPENHASH_CACHE_HASH
  852.                     e->fHash = olde->fHash;
  853.                     OpenHashEntry** where = fTable + (e->Hash() % fSlots);
  854. #else
  855.                     OpenHashEntry** where = fTable + ((*fHash)(key) % fSlots);
  856. #endif
  857.                     e->fLink = *where; // insert new entry into its bucket
  858.                     *where = e;
  859.                     
  860.                     olde = olde->fLink;
  861.                 }
  862.             }
  863.         }
  864.         old.Destroy();   // no longer need old vectors
  865.     }
  866.     return kODTrue;
  867. }
  868.  
  869. ODBoolean OpenHashTable::Grow()
  870.     // returns false only if out of memory.
  871.     // Increase the table size by 50%.  The old table is assumed to be exactly
  872.     // full, because the free list is empty, which means that every entry is in
  873.     // use.  This means we can efficiently move the old entries to the new entry
  874.     // vector by block-moving them wholesale.  The structure of the old table
  875.     // can be ignored.  Then the new entries need to be hashed into the new
  876.     // table.  The new free list begins immediately after the last copied entry.
  877. {
  878.     OpenHashSize newSlots = ((fSlots * 3) / 2) + 1; // 50% growth
  879.  
  880.     OpenHashVectors old;
  881.     if ( !NewVectors(&old, newSlots) )
  882.         return kODFalse;
  883.  
  884.     // transfer all the old entries to the new table:
  885.     memcpy(fEntries, old.fEntries, old.fSlots * sizeof(OpenHashEntry));
  886.     memcpy(fKeys, old.fKeys, old.fSlots * fKeySize);
  887.     if (fValSize)
  888.         memcpy(fValues, old.fValues, old.fSlots * fValSize);
  889.  
  890.     // the new free list starts after the last copied entry:
  891.     fFreeList = fEntries + old.fSlots;
  892.     
  893.     // hash all the copied entries into their correct table buckets:
  894.     OpenHashEntry* newEntries = fEntries - 1;
  895.     char* key = fKeys;
  896.     
  897.     while (++newEntries < fFreeList)
  898.     {
  899. #ifdef OPENHASH_CACHE_HASH
  900.         OpenHashEntry** where = fTable + (newEntries->Hash() % fSlots);
  901. #else
  902.         OpenHashEntry** where = fTable + ((*fHash)(key) % fSlots);
  903.         key += fKeySize;
  904. #endif
  905.         newEntries->fLink = *where;
  906.         *where = newEntries;
  907.     }
  908.     old.Destroy();   // no longer need old vectors
  909.     
  910.     return kODTrue;
  911. }
  912.  
  913. //==============================================================================
  914. // OpenHashTableIterator
  915. //==============================================================================
  916.  
  917. OpenHashTableIterator::OpenHashTableIterator(OpenHashTable* table)
  918.     : fTable(table), fSeed(table->Seed()), 
  919.       fBucket(0), fRef(0), fCurrent(0), fAfter(0)
  920. {
  921.     // note that fCurrent == 0 indicates the iteration is exhausted.
  922. }
  923.  
  924. OpenHashTableIterator::~OpenHashTableIterator()
  925. {
  926.     fSeed = 0;
  927.     fTable = 0;
  928. }
  929.  
  930.  
  931. ODBoolean OpenHashTableIterator::Current(void* keyPtr, void* valuePtr)
  932.     // Copies the same <key, value> pair as the last call to either
  933.     // First() or Next().  Either keyPtr or valuePtr can be set to 
  934.     // zero to suppress copying that part of the assocation.  valuePtr
  935.     // will also be ignored if <value-size> is zero.  keyPtr must 
  936.     // be a POINTER to a key, and valuePtr must be a POINTER to a value
  937.     // (e.g. if a key is struct Foo, then keyPtr must be type struct Foo*).
  938.     // keyPtr or valuePtr are ignored if there are no more associations
  939.     // (use IsNotComplete() to check).  If the table is out of sync with
  940.     // the iterator (has been modified since First() other than by
  941.     // RemoveCurrent()), then exception kODErrIteratorOutOfSync is thrown.
  942. {
  943.     if (fSeed != fTable->Seed())
  944.         THROW(kODErrIteratorOutOfSync);
  945.         
  946.     if (fCurrent)
  947.     {
  948.         fTable->CopyTo(keyPtr, valuePtr, fCurrent - fTable->fEntries);
  949.         return kODTrue;
  950.     }
  951.     else
  952.         return kODFalse;
  953. }
  954.  
  955. void OpenHashTableIterator::RemoveCurrent()
  956.     // Efficiently removes the current <key, value> pair from the table.
  957.     // This method works the same whether you are iterating with
  958.     // associations or with pointers.  If the table is out of sync with
  959.     // the iterator (has been modified since First() other than by
  960.     // RemoveCurrent()), then exception kODErrIteratorOutOfSync is thrown.
  961. {
  962.     // This function has been written so that it can be called more than 
  963.     // once for the same current assoc without doing any harm.  Also,
  964.     // Current() can be called *after* removing the current assoc
  965.     // without doing any harm (the iterator still references the current
  966.     // assoc after we unlink it from the table).
  967.     
  968.     if (fSeed != fTable->Seed())
  969.         THROW(kODErrIteratorOutOfSync);
  970.         
  971.     if (fCurrent && (*fRef != fAfter))
  972.     {
  973.         fTable->PushFreeEntry(fCurrent); // put it in free list
  974.         *fRef = fAfter;                  // unlink current entry
  975.         
  976.         // if fAfter != 0, then fRef is still an appropriate reference for
  977.         // the next entry in the iteration, so we will not need to update
  978.         // fRef in NextAssoc().
  979.  
  980.         --fTable->fCount;                // table has one fewer entries
  981.         fSeed = ++fTable->fSeed;         // changed, but *we* are in sync
  982.     }
  983. }
  984.  
  985. void OpenHashTableIterator::First(void* keyPtr, void* valuePtr)
  986.     // Resets the iteration and copies the "first" <key, value> pair 
  987.     // in the hash table.  Either keyPtr or valuePtr can be set to 
  988.     // zero to suppress copying that part of the assocation.  valuePtr
  989.     // will also be ignored if <value-size> is zero.  keyPtr must 
  990.     // be a POINTER to a key, and valuePtr must be a POINTER to a value
  991.     // (e.g. if a key is struct Foo, then keyPtr must be type struct Foo*).
  992.     // keyPtr or valuePtr are ignored if there are no more associations
  993.     // (use IsNotComplete() to check).  The iterator and the table are
  994.     // always in sync after First().
  995. {
  996.     // Note that we don't care if the seed is out of sync, because this
  997.     // operation puts us in sync.
  998.     
  999.     fSeed = fTable->Seed();
  1000.     
  1001.     OpenHashEntry** bucket = fTable->fTable;
  1002.     OpenHashEntry** end = bucket + fTable->fSlots; // one past last slot
  1003.     OpenHashEntry* e;
  1004.     fCurrent = 0L;
  1005.     
  1006.     --bucket;
  1007.     while (++bucket < end)
  1008.     {
  1009.         e = *bucket;
  1010.         if ( e ) // did we find the first entry?
  1011.         {
  1012.             fBucket = fRef = bucket;
  1013.             fCurrent = e;
  1014.             fAfter = e->fLink;
  1015.             // We need to maintain fAfter independently, because removal
  1016.             // of the current assoc overwrites fCurrent->fLink.
  1017.  
  1018.             fTable->CopyTo(keyPtr, valuePtr, e - fTable->fEntries);
  1019.             break; // stop after finding the first entry
  1020.         }
  1021.     }
  1022. }
  1023.  
  1024. void OpenHashTableIterator::Next(void* keyPtr, void* valuePtr)
  1025.     // Continues the iteration and copies the "next" <key, value> 
  1026.     // pair in the hash table.  Either keyPtr or valuePtr can be set to 
  1027.     // zero to suppress copying that part of the assocation.  valuePtr
  1028.     // will also be ignored if <value-size> is zero.  keyPtr must 
  1029.     // be a POINTER to a key, and valuePtr must be a POINTER to a value
  1030.     // (e.g. if a key is struct Foo, then keyPtr must be type struct Foo*).
  1031.     // keyPtr or valuePtr are ignored if there are no more associations
  1032.     // (use IsNotComplete() to check).  If the table is out of sync with
  1033.     // the iterator (has been modified since First() other than by
  1034.     // RemoveCurrent()), then exception kODErrIteratorOutOfSync is thrown.
  1035. {
  1036.     if (fSeed != fTable->Seed())
  1037.         THROW(kODErrIteratorOutOfSync);
  1038.         
  1039.     if (fCurrent)
  1040.     {
  1041.         if (fAfter) // more in the same bucket
  1042.         {
  1043.             if (*fRef != fAfter) // fCurrent not removed? must update fRef?
  1044.                 fRef = &fCurrent->fLink;        
  1045.             fCurrent = fAfter;
  1046.             fAfter = fAfter->fLink;
  1047.         }
  1048.         else // find the next non-empty bucket
  1049.         {
  1050.             OpenHashEntry** end = fTable->fTable + fTable->fSlots;
  1051.             OpenHashEntry* e;
  1052.             fCurrent = 0L;
  1053.             while (++fBucket < end)
  1054.             {
  1055.                 e = *fBucket;
  1056.                 if ( e ) // did we find an entry?
  1057.                 {
  1058.                     fRef = fBucket;
  1059.                     fCurrent = e;
  1060.                     fAfter = e->fLink;
  1061.                     break;
  1062.                 }
  1063.             }
  1064.         }
  1065.         if (fCurrent)
  1066.         {
  1067.             fTable->CopyTo(keyPtr, valuePtr, fCurrent - fTable->fEntries);
  1068.         }
  1069.     }
  1070. }
  1071.